﻿using Mr.Wang.FileLog.LogInterface;
using Mr.Wang.FileLog.Model;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;

namespace Mr.Wang.FileLog.PersistenceLogInfo
{
    public class FilelogPersistence : IPersistenceLog
    {

        public string FilelogPersistenceName { get; set; } = "Filelog";
        /// <summary>
        /// 是否启用缓存
        /// </summary>
        public bool EnableCache { get; set; } = true;

        /// <summary>
        /// 写入日志最大时间间隔
        /// </summary>
        public TimeSpan TimeOut { get; set; } = TimeSpan.FromSeconds(3);

        /// <summary>
        /// 写入日志的信息
        /// </summary>
        private ConcurrentDictionary<string, FileLogInfo> writedLogFileSizeDic = new ConcurrentDictionary<string, FileLogInfo>();

        /// <summary>
        /// 最大日志缓存长度
        /// </summary>
        public int MaxStringBuilderLength { get; set; } = 1000;

        private string rootDirectory = AppDomain.CurrentDomain.BaseDirectory;

        /// <summary>
        /// 文件日志根节点
        /// </summary>
        public string RootDirectory
        {
            get { return rootDirectory; }
            set
            {
                if (!Directory.Exists(value))
                {
                    try
                    {
                        Directory.CreateDirectory(value);
                    }
                    catch (Exception ex)
                    {

                        throw ex;
                    }
                }
                rootDirectory = value;
            }
        }

        //ReaderWriterLockSlim
        private ReaderWriterLockSlim rwLockS = new ReaderWriterLockSlim();



        public FilelogPersistence(string managerName = null)
        {
            if (!string.IsNullOrEmpty(managerName))
            {
                this.FilelogPersistenceName = managerName;
            }
        }

        public void MarshalLog(LogInfo info)
        {
            if (this.EnableCache)
            {
                var localLogInfo = GetAndCreateLogInfo(info.LoggerName);
                localLogInfo.CacheSb.Append(FormateLogInfoOutPut(info));
                //是否需要写入文本
                if (localLogInfo.CacheSb.Length > this.MaxStringBuilderLength || (DateTime.Now - localLogInfo.LastWriteTime) > this.TimeOut)
                {
                    WriteFileLog(info.LoggerName, localLogInfo.CacheSb.ToString());
                    localLogInfo.CacheSb.Clear();
                }
            }
            else
            {
                PromptlyLog(info);
            }
        }

        public void CheckIsWriteLog()
        {
            foreach (var kvp in writedLogFileSizeDic)
            {
                if (kvp.Value.CacheSb.Length > this.MaxStringBuilderLength || (DateTime.Now - kvp.Value.LastWriteTime) > this.TimeOut)
                {
                    WriteFileLog(kvp.Key, kvp.Value.CacheSb.ToString());
                    kvp.Value.CacheSb.Clear();
                }
            }
        }

        public void PromptlyLog(LogInfo info)
        {
            WriteFileLog(info.LoggerName, FormateLogInfoOutPut(info));
        }


        private string FormateLogInfoOutPut(LogInfo info)
        {
            if (info == null)
            {
                return string.Empty;
            }
            StringBuilder logInfoSb = new StringBuilder();
            logInfoSb.Append(info.OccTime.ToString(Util.formaterDateTimeOutPut));
            logInfoSb.Append($" [{info.CurlogLeve}]\r\n");
            logInfoSb.Append(info.Message);
            logInfoSb.AppendLine();
            logInfoSb.AppendLine();
            return logInfoSb.ToString();
        }

        private void WriteFileLog(string logName, string mgs)
        {
            if (string.IsNullOrEmpty(mgs))
            {
                return;
            }
            rwLockS.EnterWriteLock();
            try
            {
                FileLogInfo fileLogInfo = GetAndCreateLogInfo(logName);
                File.AppendAllText(fileLogInfo.FullFilePath, mgs);
                fileLogInfo.LastWriteTime = DateTime.Now;
                fileLogInfo.FileSize += mgs.Length;
                if (fileLogInfo.FileSize > 2.5 * 1024 * 1024)
                {
                    fileLogInfo.ChangeFulllePahtBySize();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                rwLockS.ExitWriteLock();
            }
        }


        private FileLogInfo GetAndCreateLogInfo(string logName)
        {
            FileLogInfo info;
            if (!writedLogFileSizeDic.ContainsKey(logName))
            {
                info = new FileLogInfo
                {
                    FileSize = 0,
                    CacheSb = new StringBuilder(),
                    LastWriteTime = DateTime.Now,
                };
                info.FullFilePath = Path.Combine(this.RootDirectory, Util.GetValidFileName(FilelogPersistenceName), Util.GetValidFileName(logName), Util.GetValidFileName(DateTime.Now.ToString(Util.formaterDateTimeOutPut)) + ".log");
                string curDirPath = Path.GetDirectoryName(info.FullFilePath);
                if (!Directory.Exists(curDirPath))
                {
                    Directory.CreateDirectory(curDirPath);
                }
                writedLogFileSizeDic.TryAdd(logName, info);
            }
            else
            {
                info = writedLogFileSizeDic[logName];
            }
            return info;
        }

        public void Dispose()
        {

        }

        public void PromtlyWrite()
        {
            foreach (var kvp in writedLogFileSizeDic)
            {
                WriteFileLog(kvp.Key, kvp.Value.CacheSb.ToString());
                kvp.Value.CacheSb.Clear();
            }
        }
    }

    class FileLogInfo
    {
        public int FileSize { get; set; }
        public string FullFilePath { get; set; }
        public StringBuilder CacheSb { get; set; }
        public DateTime LastWriteTime { get; set; }

        public void ChangeFulllePahtBySize()
        {
            FullFilePath = Path.Combine(Path.GetDirectoryName(FullFilePath), Util.GetValidFileName(DateTime.Now.ToString(Util.formaterDateTimeOutPut)) + ".log");
            FileSize = 0;
        }


    }
}
